﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Raise3000.Code.Foundation;
using Raise3000.Code.Attribute_Calculator;

namespace Raise3000.Code.HandHistoryLoader
{
    public enum HandHistorySection
    {
        eHEADER_SECTION,
        ePLAYERINFO_SECTION,
        eBLIND_SECTION,
        eCARDDEALT_SECTION,
        eSTARTINGHAND_SECTION,
        ePREFLOP_SECTION,
        eFLOP_SECTION,
        eTURN_SECTION,
        eRIVER_SECTION,
        eNOSHOWDOWN_SECTION,
        eSHOWDOWN_SECTION,
        eSUMMARY_SECTION
    };

    public class AbsolutePokerHandHistoryLoader : IHandHistoryLoader
    {
        Tokenizer tokenizer_ = null;

        int numberOfSeats_ = 0;

        public AbsolutePokerHandHistoryLoader(List<string> handHistories, CalculatorManager calculatorManager, int numberOfSeats)
            : base(handHistories, calculatorManager)
        {
            tokenizer_ = new Tokenizer();
            numberOfSeats_ = numberOfSeats;
        }
        
        protected override bool ReadNextHandFromStream(StreamReader streamReader)
        {
            bool handSucessfullyProcessed = false;

            string firstLine = streamReader.ReadLine();
            string secondLine = streamReader.ReadLine();

            bool handSeparatorIsOK = (firstLine == "################################################################################################") &&
                                     (secondLine != null);
            if (handSeparatorIsOK)
            {
                HandContext handContext = new HandContext(numberOfSeats_);

                HandHistoryParserHelper helper = new HandHistoryParserHelper(numberOfSeats_);

                // temporaly data - if we collected all information then will be assigned to HandContext
                List<string> listOfPlayerInCurrentHand = new List<string>();
                List<int> listOfPosition = new List<int>();
                List<double> listOfStack = new List<double>();

                // The first line of hand describe some general info - we are not gonna to process it.
                HandHistorySection section = HandHistorySection.eHEADER_SECTION; // in which part of hand we are
                string nextLine = "";

                while (!handSucessfullyProcessed)
                {
                    switch (section)
                    {
                        case HandHistorySection.eHEADER_SECTION:
                            {
                                nextLine = streamReader.ReadLine(); // second line

                                // ectract some info ; who is the dealer and move on the seat list.
                                tokenizer_.ReInitTokenizer(nextLine);
                                //string seatToken = tokenizer_.GetNextToken(8); // seatToken == "Seat"
                                while (tokenizer_.GetNextToken() != "Seat") { }

                                string dealerPosition = tokenizer_.GetNextToken(2);

                                handContext.DealerPosition = long.Parse(dealerPosition);

                                section = HandHistorySection.ePLAYERINFO_SECTION;
                                break;
                            }
                        case HandHistorySection.ePLAYERINFO_SECTION:
                            {
                                // in this section we extract players name, where they seat, and their stack size.
                                nextLine = streamReader.ReadLine();
                                
                                tokenizer_.ReInitTokenizer(nextLine);
                                // if the line begins with "Seat" that means we found another player.
                                string token = tokenizer_.GetNextToken();
                                if (token == "Seat")
                                {
                                    int position = int.Parse(tokenizer_.GetNextToken());

                                    string dummy = tokenizer_.GetNextToken();
                                    // maybe can here write  a check: dummy should equal to '-'

                                    // this will advance the tokenizer!!!
                                    string playerName = TryExtractPlayerName(); 
                                    
                                    dummy = tokenizer_.GetNextToken(2); // here dummy should equal to : $

                                    double stack = double.Parse(dummy);

                                    // here we just add to temporaly list because this player may have sitout.
                                    listOfPlayerInCurrentHand.Add(playerName);
                                    listOfPosition.Add(position);
                                    listOfStack.Add(stack);
                                }
                                else
                                {
                                    // this section is end - move on
                                    section = HandHistorySection.eBLIND_SECTION;
                                }
                                break;
                            }
                        case HandHistorySection.eBLIND_SECTION:
                            {
                                // This section describe which player post some sort of blinds.
                                // Typically the small and big blinds. ( but may have other blinds or 7.2 bet )
                                if ( (nextLine == "No small blind is posted due to player leave") ||
                                     (nextLine == "No small blind is posted due to player sit out") )
                                {
                                    nextLine = streamReader.ReadLine();
                                }

                                tokenizer_.ReInitTokenizer(nextLine);

                                string nextToken = tokenizer_.GetNextToken();
                                if (nextToken != "***")
                                {
                                    // Typicly a line is: "PCSURGEON - Posts small blind $0.05"
                                    tokenizer_.ReInitTokenizer(nextLine);

                                    string player = TryExtractPlayerName();
                                    nextToken = tokenizer_.GetNextToken();
                                    if (nextToken == "Posts")
                                    {
                                        // so, <player> Posts small/big blind : $x.xx
                                        string blind = tokenizer_.GetNextToken(4);
                                        long b = 0;
                                        try
                                        {
                                            b = (long)(double.Parse(blind) * 100);
                                        }
                                        catch (System.FormatException fe)
                                        {                                            
                                            // can happen if line looks like: <player> - Posts $0.10 dead $0.05
                                            tokenizer_.ReInitTokenizer(nextLine);

                                            blind = tokenizer_.GetNextToken(5);
                                            b = (long)(double.Parse(blind) * 100);
                                        }

                                        helper.AddPlayerAndChips(player, b);
                                    }
                                    else if (nextToken == "sitout")
                                    {
                                        // this player does not contest in this hand, 
                                        // should be unregister - remove from temporaly data.
                                        int indexOf = listOfPlayerInCurrentHand.IndexOf(player);

                                        listOfPlayerInCurrentHand.RemoveAt(indexOf);
                                        listOfPosition.RemoveAt(indexOf);
                                        listOfStack.RemoveAt(indexOf);
                                    }
                                    else if (nextToken == "72")
                                    {
                                        nextToken = tokenizer_.GetNextToken(); // Proposition
                                        nextToken = tokenizer_.GetNextToken(); // Bet
                                        nextToken = tokenizer_.GetNextToken(); // returned

                                        if (nextToken == "returned")
                                        {
                                            // means this player sitout and will not posts blind.
                                            int indexOf = listOfPlayerInCurrentHand.IndexOf(player);

                                            listOfPlayerInCurrentHand.RemoveAt(indexOf);
                                            listOfPosition.RemoveAt(indexOf);
                                            listOfStack.RemoveAt(indexOf);

                                        }
                                    }

                                    nextLine = streamReader.ReadLine();
                                    if (nextLine == "No small blind is posted due to player sit out")
                                    {
                                        nextLine = streamReader.ReadLine();
                                    }
                                }
                                else
                                {
                                    // Nice, we have been processed this section.
                                    // Check: nextLine should be: "*** POCKET CARDS ***"
                                    section = HandHistorySection.eCARDDEALT_SECTION;
                                }
                                break;
                            }
                        case HandHistorySection.eCARDDEALT_SECTION:
                            {
                                // This section describe the hole cards dealt to Hero.
                                nextLine = streamReader.ReadLine();
                                tokenizer_.ReInitTokenizer(nextLine);

                                // The first two token is: "Dealt to", 3rd is Hero :)
                                string player = tokenizer_.GetNextToken(3);
                                tokenizer_.GetNextToken(); // "["

                                string holeCards1 = tokenizer_.GetNextToken(); // first card
                                string holeCards2 = tokenizer_.GetNextToken(); // second card

                                // these information is not public information, so there is no sense to set on handcontext
                                // we will dispatch between calculators.

                                // Done with this section.
                                section = HandHistorySection.eSTARTINGHAND_SECTION;
                                break;
                            }
                        case HandHistorySection.eSTARTINGHAND_SECTION:
                            {
                                // now, we can initialize HandContext;
                                for (int idx = 0; idx < listOfPlayerInCurrentHand.Count; ++idx)
                                {
                                    string playerName = listOfPlayerInCurrentHand[idx];
                                    int position = listOfPosition[idx];
                                    double stack = listOfStack[idx];

                                    handContext.Initialize(playerName, position, stack);
                                }

                                calculatorManager_.SetHandContext(handContext);

                                // starting actions
                                calculatorManager_.OnStartHand();

                                section = HandHistorySection.ePREFLOP_SECTION;
                                break;
                            }
                        case HandHistorySection.ePREFLOP_SECTION:
                            {
                                // This is the preflop section
                                calculatorManager_.StartPreflopActions();
                                
                                bool isMorePreflopAction = true;
                                while (isMorePreflopAction)
                                {
                                    nextLine = streamReader.ReadLine();
                                    tokenizer_.ReInitTokenizer(nextLine);

                                    string token = tokenizer_.GetNextToken();

                                    if (token != "***")
                                    {
                                        tokenizer_.ReInitTokenizer(nextLine);
                                        token = TryExtractPlayerName();

                                        if (listOfPlayerInCurrentHand.IndexOf(token) >= 0)
                                        {
                                            AnalyzeActions(nextLine, helper);
                                        }
                                        else
                                        {
                                            isMorePreflopAction = false;
                                        }
                                    }
                                    else
                                    {
                                        isMorePreflopAction = false;
                                    }
                                }

                                calculatorManager_.EndPreflopActions();
                                // test wether the hand is ended here, or continue with the flop.
                                if (helper.NumberOfPlayerInHand() > 1 || helper.AllinSituation)
                                {
                                    section = HandHistorySection.eFLOP_SECTION;
                                }
                                else
                                {                                    
                                    // This hand is over, go to Show Down section.
                                    section = HandHistorySection.eNOSHOWDOWN_SECTION;
                                }
                                break;
                            };
                        case HandHistorySection.eFLOP_SECTION:
                            {
                                // This is the flop section.
                                helper.FlopSeen = true;
                                
                                tokenizer_.ReInitTokenizer(nextLine);

                                tokenizer_.GetNextToken(4);                                

                                Card[] flop = new Card[3];
                                flop[0] = new Card(tokenizer_.GetNextToken());
                                flop[1] = new Card(tokenizer_.GetNextToken());
                                flop[2] = new Card(tokenizer_.GetNextToken());

                                calculatorManager_.SetFlop(flop);
                                
                                // Flop section.
                                calculatorManager_.StartFlopActions();

                                bool isMoreFlopAction = true;
                                while (isMoreFlopAction)
                                {
                                    nextLine = streamReader.ReadLine();
                                    tokenizer_.ReInitTokenizer(nextLine);

                                    string token = tokenizer_.GetNextToken();

                                    if (token != "***")
                                    {
                                        tokenizer_.ReInitTokenizer(nextLine);
                                        token = TryExtractPlayerName();

                                        if (listOfPlayerInCurrentHand.IndexOf(token) >= 0)
                                        {
                                            AnalyzeActions(nextLine, helper);
                                        }
                                        else
                                        {
                                            isMoreFlopAction = false;
                                        }
                                    }
                                    else
                                    {
                                        isMoreFlopAction = false;
                                    }
                                }

                                calculatorManager_.EndFlopActions();

                                if (helper.NumberOfPlayerInHand() > 1 || helper.AllinSituation)
                                {
                                    section = HandHistorySection.eTURN_SECTION;
                                }
                                else
                                {
                                    section = HandHistorySection.eNOSHOWDOWN_SECTION;
                                }
                                break;
                            }
                        case HandHistorySection.eTURN_SECTION:
                            {
                                // This is the turn section                                
                                tokenizer_.ReInitTokenizer(nextLine);

                                tokenizer_.GetNextToken(9);

                                Card turn = new Card(tokenizer_.GetNextToken());

                                calculatorManager_.SetTurn(turn);
                                // Turn action
                                calculatorManager_.StartTurnActions();

                                bool isMoreTurnAction = true;
                                while (isMoreTurnAction)
                                {
                                    nextLine = streamReader.ReadLine();
                                    tokenizer_.ReInitTokenizer(nextLine);

                                    string token = tokenizer_.GetNextToken();

                                    if (token != "***")
                                    {
                                        tokenizer_.ReInitTokenizer(nextLine);
                                        token = TryExtractPlayerName();

                                        if (listOfPlayerInCurrentHand.IndexOf(token) >= 0)
                                        {
                                            AnalyzeActions(nextLine, helper);
                                        }
                                        else
                                        {
                                            isMoreTurnAction = false;
                                        }
                                    }
                                    else
                                    {
                                        isMoreTurnAction = false;
                                    }
                                }

                                calculatorManager_.EndTurnActions();

                                if (helper.NumberOfPlayerInHand() > 1 || helper.AllinSituation)
                                {
                                    section = HandHistorySection.eRIVER_SECTION;
                                }
                                else
                                {
                                    section = HandHistorySection.eNOSHOWDOWN_SECTION;
                                }
                                break;
                            }
                        case HandHistorySection.eRIVER_SECTION:
                            {
                                // This is the river section                                
                                tokenizer_.ReInitTokenizer(nextLine);

                                tokenizer_.GetNextToken(10);

                                Card river = new Card(tokenizer_.GetNextToken());
                                // River section
                                calculatorManager_.StartRiverActions();

                                bool isMoreRiverAction = true;
                                while (isMoreRiverAction)
                                {
                                    nextLine = streamReader.ReadLine();
                                    tokenizer_.ReInitTokenizer(nextLine);

                                    string token = tokenizer_.GetNextToken();

                                    if (token != "***")
                                    {
                                        tokenizer_.ReInitTokenizer(nextLine);
                                        token = TryExtractPlayerName();

                                        if (listOfPlayerInCurrentHand.IndexOf(token) >= 0)
                                        {
                                            AnalyzeActions(nextLine, helper);
                                        }
                                        else
                                        {
                                            isMoreRiverAction = false;
                                        }
                                    }
                                    else
                                    {
                                        isMoreRiverAction = false;
                                    }
                                }

                                calculatorManager_.EndRiverActions();

                                if (helper.NumberOfPlayerInHand() > 1)
                                {
                                    section = HandHistorySection.eSHOWDOWN_SECTION; // no more betting round - let's show down.
                                }
                                else
                                {
                                    section = HandHistorySection.eNOSHOWDOWN_SECTION;
                                }

                                break;
                            }
                        case HandHistorySection.eNOSHOWDOWN_SECTION:
                            {
                                // Show Down section - when there is no really show dons.(all opponents folded)
                                nextLine = streamReader.ReadLine(); // Show Down
                                nextLine = streamReader.ReadLine(); // Players shows or not his hand.
                                nextLine = streamReader.ReadLine(); // Collects the pot.

                                section = HandHistorySection.eSUMMARY_SECTION;
                                break;
                            }
                        case HandHistorySection.eSHOWDOWN_SECTION:
                            {
                                // Show Down section.
                                int nrOfPlayers = helper.NumberOfPlayerInHand();
                                for (int idx = 1; idx <= nrOfPlayers; ++idx)
                                {
                                    nextLine = streamReader.ReadLine();
                                }

                                bool summaryWasReached = false;
                                while (!summaryWasReached)
                                {
                                    // Who win
                                    nextLine = streamReader.ReadLine(); // <player> collects <pot> from main pot.

                                    summaryWasReached = (nextLine == "*** SUMMARY ***");
                                }

                                section = HandHistorySection.eSUMMARY_SECTION; // move to summmary.
                                break;
                            }
                        case HandHistorySection.eSUMMARY_SECTION:
                            {
                                // The summary section. - simply ignore this section.
                                 
                                if (!(nextLine == "*** SUMMARY ***"))
                                {
                                    nextLine = streamReader.ReadLine(); // *** Summary ***
                                }
                                
                                nextLine = streamReader.ReadLine(); // Total pot and rake

                                if (helper.FlopSeen)
                                {
                                    nextLine = streamReader.ReadLine(); // The board.
                                }

                                // and the players
                                for (int idx = 0; idx < listOfPlayerInCurrentHand.Count; ++idx)
                                {
                                    nextLine = streamReader.ReadLine();
                                    tokenizer_.ReInitTokenizer(nextLine);
                                    if (tokenizer_.GetNextToken() != "Seat")                                   
                                    {
                                        nextLine = streamReader.ReadLine();
                                    }
                                }

                                handSucessfullyProcessed = true; // The hand is processed.:)
                                break;
                            }
                    };
                }                
            }

            return handSucessfullyProcessed;
        }

        private void AnalyzeActions(string nextLine, HandHistoryParserHelper helper)
        {
            tokenizer_.ReInitTokenizer(nextLine);

            string playerName = TryExtractPlayerName();

            string move = tokenizer_.GetNextToken(); // skip '-'
            if (move == "All")
            {
                // Player is allin
                string token = tokenizer_.GetNextToken(2); // token == "In"
                if (tokenizer_.GetNextToken(2) == "Raise")
                {
                    token = tokenizer_.GetNextToken(1);
                    move = "Raises";
                }
                else
                {
                    move = "Calls";
                    tokenizer_.ReInitTokenizer(nextLine);
                    tokenizer_.GetNextToken(5); //seek to the right position                    
                }
            }
            if (move == "returned")
            {
                double amount = double.Parse(tokenizer_.GetNextToken(3));
                helper.AddPlayerAndChips(playerName, -(long)(amount * 100));
            }

            PlayerAction playerAction = null;

            ActionEnum action = helper.ConvertToActionEnum(move);
            switch (action)
            {
                case ActionEnum.eFOLD:
                    {
                        // fold has no amount
                        playerAction = new PlayerAction(playerName, action, 0);
                        helper.RemovePlayer(playerName);
                        break;
                    }
                case ActionEnum.eCHECK:
                    {
                        playerAction = new PlayerAction(playerName, action, 0);
                        break;
                    }
                case ActionEnum.eCALL:
                    {
                        double amount = double.Parse(tokenizer_.GetNextToken(2));
                        playerAction = new PlayerAction(playerName, action, amount);

                        helper.AddPlayerAndChips(playerName, (long)(amount * 100));
                        break;
                    }
                case ActionEnum.eBET:
                    {
                        double amount = double.Parse(tokenizer_.GetNextToken(2));
                        playerAction = new PlayerAction(playerName, action, amount);

                        helper.AddPlayerAndChips(playerName, (long)(amount * 100));
                        break;
                    }
                case ActionEnum.eRAISE:
                    {
                        double amount = double.Parse(tokenizer_.GetNextToken(2));
                        playerAction = new PlayerAction(playerName, action, amount);

                        helper.AddPlayerAndChips(playerName, (long)(amount * 100));
                        break;
                    }                    
            };

            if (playerAction != null)
            {
                bool allFolded = helper.AllFold;
                helper.AllFold = (allFolded && (action == ActionEnum.eFOLD));

                calculatorManager_.DispatchAction(playerAction);
            }
        }

        private string TryExtractPlayerName()
        {
            string playerName = "";

            string token = "";
            while ((token != "-") && (token != "("))
            {
                token = tokenizer_.GetNextToken();                
                if ((token != "-") && (token != "("))
                {
                    if (playerName != "")
                    {
                        playerName += " ";
                    }
                    playerName += token;
                }
            }

            return playerName;
        }
    }
}
